Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding new base_query_class arg for associations. #1006

Merged
merged 6 commits into from
Mar 23, 2024
Merged

Adding new base_query_class arg for associations. #1006

merged 6 commits into from
Mar 23, 2024

Conversation

jwoertink
Copy link
Member

@jwoertink jwoertink commented Feb 24, 2024

Fixes #85

This PR adds a new arg that allows you to set the query class used for querying your association. This can be used in conjunction with soft_deleted records to ensure every preload you do only preloads the kept records.

class User < BaseModel
  table do
    has_many transactions : Transaction, base_query_class: TransactionQuery
    has_many follows : Follow, foreign_key: :followee_id, base_query_class: FollowQuery
    has_many followers : User, through: [:follows, :follower]
  end
end

class TransactionQuery < Transaction::BaseQuery
  include Avram::SoftDelete::Query

  def initialize
    defaults &.only_kept
  end
end

# This only preloads the kept records
UserQuery.new.preload_transactions

The default base_query_class is always T::BaseQuery, but with soft_deleted models, you would have to either monkey-patch T::BaseQuery, or skip using preloads to avoid cases where you would preload a soft deleted record.

Currently this PR is only adding in has_many and has_many :through associations. If this works out, we can work out has_one, but I'm not sure if belongs_to would need this or not 🤔

Side note

There is an edge case that comes up with this. Each association has an extra _count method that returns the total count of that association. It uses the base_query_class to run a select_count. If you use the escape hatch to preload more records than the base query uses, your _count method will return more or less than what you've preloaded.

u = UserQuery.new.preload_transactions.first
u.transactions_count #=> 10
u.transactions.size #=> 10

# preload ALL transactions, even soft deleted ones
u = UserQuery.new.preload_transactions(Transaction::BaseQuery.new)first
u.transactions_count #=> 10
u.transactions.size #=> 20

Side note 2

How the joins are built when using "has many through" is a bit complex. You generally preload your has_many query, but then that one needs to preload the join internally. If you need an escape hatch out of that default setup, I've added some new args to the preload methods here.

# This preload followers and internally the kept follow records
UserQuery.new.preload_followers

# If you need the escape hatch, you have to pass in the base query
# This will now preload all records both kept and soft deleted
UserQuery.new.preload_followers(User::BaseQuery.new, through: Follow::BaseQuery.new)

@jwoertink jwoertink marked this pull request as ready for review March 9, 2024 21:58
@@ -1247,7 +1247,7 @@ describe Avram::Queryable do
UserQuery.new.select_count.should eq 10
# NOTE: we don't test rows_affected here because this isn't
# available with a truncate statement
UserQuery.truncate
UserQuery.truncate(cascade: true)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This had to change because I added the has_many transactions to User, and now it was necessary.

@jwoertink jwoertink merged commit e0ca6de into main Mar 23, 2024
9 checks passed
@jwoertink jwoertink deleted the issues/85 branch March 23, 2024 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow specifying conditionals in associations
1 participant